package br.requisiteworks.controller;

import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.List;

import javax.validation.Valid;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.propertyeditors.CustomDateEditor;
import org.springframework.security.access.annotation.Secured;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.context.request.WebRequest;

import br.requisiteworks.modelo.Cliente;
import br.requisiteworks.modelo.Projeto;
import br.requisiteworks.modelo.Time;
import br.requisiteworks.modelo.Projeto.Status;
import br.requisiteworks.modelo.service.ClienteService;
import br.requisiteworks.modelo.service.ProjetoService;
import br.requisiteworks.modelo.service.TimeService;
import br.requisiteworks.seguranca.modelo.UsuarioLogado;
import br.requisiteworks.util.persistencia.ErroBancoDados;
import br.requisiteworks.util.persistencia.ResultadoVazio;

/**
 * Implementacao de um controller que controla as operacoes relacionadas ao
 * modulo de {@link Projeto}s
 * 
 * @author Leandro Chaves
 * @version 1, 06/07/2010
 */
@Controller
@Secured("ROLE_GERENTE")
@RequestMapping("/projeto")
public class ProjetoController {

	@Autowired
	private ProjetoService projetoService;

	@Autowired
	private ClienteService clienteService;

	@Autowired
	private TimeService timeService;

	private static final Projeto VAZIO = new Projeto();

	@ModelAttribute
	public void preCarregaProjeto(Projeto projeto, BindingResult result)
			throws ResultadoVazio {

		if (result.hasErrors()) {
			return;
		}

		Time time = projeto.getTime();
		Cliente cliente = projeto.getCliente();

		if (cliente != null && cliente.getNumero() > 0) {
			cliente = this.clienteService.carregarCliente(cliente.getNumero());
			projeto.setCliente(cliente);
		} else {
			projeto.setCliente(null);
		}

		if (time != null && time.getNumero() > 0) {
			time = this.timeService.carregarTime(time.getNumero());
			projeto.setTime(time);
		} else {
			projeto.setTime(null);
		}
	}

	@RequestMapping(value = "/pesquisar.do", method = RequestMethod.GET)
	public List<Projeto> pesquisar(ModelMap model) {
		try {
			return this.projetoService.listarProjetos();
		} catch (ResultadoVazio e) {
			model.addAttribute("erro", e.getMessage());
			return null;
		}
	}

	@RequestMapping(value = "/pesquisar.do", method = RequestMethod.POST)
	public List<Projeto> pesquisar(@RequestParam("q") String query,
			ModelMap model) {

		if (query.matches("\\d+")) {
			try {
				return Arrays.asList(this.projetoService
						.carregarProjeto(Integer.parseInt(query)));
			} catch (ResultadoVazio e) {
				model.addAttribute("erro", e.getMessage());
				return null;
			} catch (NumberFormatException e) {
				// Nao deve ocorre devido a expressao regular
				return null;
			}
		} else {
			try {
				return this.projetoService.procuraNome(query.trim());
			} catch (ResultadoVazio e) {
				model.addAttribute("erro", e.getMessage());
				return null;
			}
		}

	}

	@RequestMapping(value = "/util/pesquisar.do", method = RequestMethod.GET)
	public void pesquisar() {
	}

	@RequestMapping(value = "/util/pesquisar.do", method = RequestMethod.POST)
	public List<Projeto> pesquisarProjetos(@RequestParam("q") String query,
			ModelMap model) {

		if (query.matches("\\d+")) {
			try {
				return Arrays.asList(this.projetoService
						.carregarProjeto(Integer.parseInt(query)));
			} catch (ResultadoVazio e) {
				model.addAttribute("erro", e.getMessage());
				return null;
			} catch (NumberFormatException e) {
				// Nao deve ocorre devido a expressao regular
				return null;
			}
		} else {
			try {
				return this.projetoService.procuraNome(query.trim());
			} catch (ResultadoVazio e) {
				model.addAttribute("erro", e.getMessage());
				return null;
			}
		}

	}

	@RequestMapping(value = "/{numero}/detalhes.do", method = RequestMethod.GET)
	public String atualizar(@PathVariable("numero") Integer numero,
			ModelMap model) throws ResultadoVazio {
		model.addAttribute("status", Projeto.Status.values());
		model.addAttribute("projeto", this.projetoService
				.carregarProjeto(numero));
		return "projeto/detalhes";
	}

	@RequestMapping(value = "/atualizar.do", method = RequestMethod.POST)
	public String atualizar(@Valid Projeto novoProjeto, BindingResult result,
			ModelMap model) throws ResultadoVazio {

		if (result.hasErrors()) {

			return novoProjeto.getNumero() + "/detalhes.do";
		}
		if (novoProjeto.getInicio().after(novoProjeto.getTermino())) {
			result.addError(new FieldError("projeto", "inicio",
					"O inicio nao pode ser apos o termino do projeto"));
			return novoProjeto.getNumero() + "/detalhes.do";
		}

		if (novoProjeto.getInicio().after(novoProjeto.getTerminoEstimado())) {
			result
					.addError(new FieldError("projeto", "inicio",
							"O inicio nao pode ser apos o termino estimado do projeto"));
			return novoProjeto.getNumero() + "/detalhes.do";
		}

		try {
			Projeto projeto = this.projetoService.carregarProjeto(novoProjeto
					.getNumero());

			projeto.setNome(novoProjeto.getNome());
			projeto.setInicio(novoProjeto.getInicio());
			if (novoProjeto.getStatus() != Status.Finalizado) {
				projeto.setTermino(novoProjeto.getTermino());
				// projeto.setTerminoEstimado(novoProjeto.getTerminoEstimado());
			}
			projeto.setDescricao(novoProjeto.getDescricao());
			projeto.setCliente(novoProjeto.getCliente());
			projeto.setTime(novoProjeto.getTime());
			projeto.setStatus(novoProjeto.getStatus());
			projeto.setNotificacao(novoProjeto.isNotificacao());
			projeto.setUsuarioRegistro(UsuarioLogado.getUsername());

			this.projetoService.atualizarProjeto(projeto);

		} catch (Throwable e) {
			model.addAttribute("erro", e.getMessage());
			return this.atualizar(novoProjeto.getNumero(), model);
		}

		return "redirect:pesquisar.do";
	}

	@RequestMapping(value = "/deletar.do", method = RequestMethod.GET)
	public String deletar(@RequestParam("numero") Integer numero, ModelMap model) {
		try {
			Projeto projeto = this.projetoService.carregarProjeto(numero);

			this.projetoService.deletaProjeto(projeto);
		} catch (ErroBancoDados e) {
			model.addAttribute("erro", e.getMessage());
		}

		return "redirect:pesquisar.do";
	}

	@RequestMapping(value = "/adicionar.do", method = RequestMethod.GET)
	public Projeto adicionar(ModelMap model) {
		model.addAttribute("statusList", Projeto.Status.values());
		return VAZIO;
	}

	@RequestMapping(value = "/adicionar.do", method = RequestMethod.POST)
	public String adicionar(@Valid Projeto projeto, BindingResult result,
			ModelMap model) {

		if (result.hasErrors()) {
			return null;
		}

		if (projeto.getInicio().after(projeto.getTerminoEstimado())) {
			result
					.addError(new FieldError("projeto", "inicio",
							"O inicio nao pode ser apos o termino estimado do projeto"));
			return null;
		}

		projeto.setUsuarioRegistro(UsuarioLogado.getUsername());

		try {
			this.projetoService.salvarProjeto(projeto);
		} catch (Throwable e) {
			model.addAttribute("erro", e.getMessage());
			return null;
		}

		return "redirect:pesquisar.do";
	}

	@InitBinder
	public void initBinder(WebDataBinder binder, WebRequest request) {
		// Date format
		SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy",
				request.getLocale());
		dateFormat.setLenient(false);
		CustomDateEditor customDateEditor = new CustomDateEditor(dateFormat,
				true);
		binder.registerCustomEditor(java.util.Date.class, customDateEditor);
	}
}
